﻿using System;
using System.ComponentModel;
using ActionLogic;
using BehaviorModel.Block.RandomGenerator;
using BehaviorModel.Statistic;
using Model;
using Model.Block;
using log4net;
using BehaviorModel.XmlSerializer;

namespace BehaviorModel.Block.Generator
{
    /// <summary>
    /// Describes implementation of Generator.
    /// </summary>
    public class Generator : IGenerator
    {
        #region Fields
        
        private Request _nextRequest;
        
        private RandomGeneratorType _generatorType;
       
        private IReceiverBlock _receiverBlock;

        #endregion

        #region Properties

        #region IBlock

        public Guid Id { get; private set; }

        public BlockType Type { get { return BlockType.Generator; } }

        #endregion

        #region IExecutable

        public event Action<PriorityAction> OnActionExecuted;

        #endregion

        #region IGenerator
        
        public int StartTime { get; set; }

        public GeneratorStorage Storage { get; private set; }

        public IBlockStatistic Statistic { get; private set; }
        
        public IRandomGenerator RequestGenerator { get; set; }

        public RandomGeneratorType GeneratorType
        {
            get { return _generatorType; }
            set
            {
                if (_generatorType != value)
                {
                    _generatorType = value;
                    RequestGenerator = RandomGeneratorFactory.GetInstance().Create(_generatorType);
                    RaisePropertyChanged("GeneratorType");
                }
            }
        }

        #endregion

        #region INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #endregion

        #region Constructor

        public Generator()
        {
            Id = Guid.NewGuid();
            Storage = new GeneratorStorage();
            GeneratorType = RandomGeneratorType.Poisson;
            StartTime = 0;
            Statistic = new GeneratorStatistic(this);
        }

        #endregion

        #region Methods

        private void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #region IBlock

        public void Initialize()
        {
            Storage = new GeneratorStorage();
            Statistic = new GeneratorStatistic(this);
            CreateStartRequest();
            RaiseActionExecuted();
        }

        private void CreateStartRequest()
        {
            _nextRequest = RequestPool.GetInstance().GetFreeRequest();
            _nextRequest.InitTime = StartTime;
        }

        #endregion

        #region ISenderBlock

        public void ConnectReceiverBlock(IReceiverBlock receiverBlock)
        {
            _receiverBlock = receiverBlock;

            //logger.Info("Generator is adding ReceivindBlock" + block.GetType().ToString() + " with id: " + block.Id);
        }

        public void DisconnectReceiverBlock(IReceiverBlock receiverBlock)
        {
            _receiverBlock = null;
        }

        public void Send(Request request)
        {
            if (_receiverBlock == null) return;
            _receiverBlock.Receive(request);
        }

        #endregion

        public void Generate()
        {
            Send(_nextRequest);
            GenerateNextRequest();
            IncRequestCounter();
            RaiseActionExecuted();
        }

        private void GenerateNextRequest()
        {
            var nextInitTime = RequestGenerator.GetNext(_nextRequest.InitTime);

            _nextRequest = RequestPool.GetInstance().GetFreeRequest();
            _nextRequest.InitTime = nextInitTime;
        }

        private void IncRequestCounter()
        {
            Storage.CountOfGenerated++;
        }

        private void RaiseActionExecuted()
        {
            if (OnActionExecuted != null)
            {
                OnActionExecuted(new PriorityAction(Generate, _nextRequest.InitTime, false));
            }
        }

        #endregion

    }
}
